﻿using Microsoft.Data.Edm;
using Microsoft.Data.OData;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using System.Web.Http.OData;
using System.Web.Http.OData.Query;
using System.Web.Http.Results;
using WooCoo.DataPortal.Server;
using WooCoo.ObjectModel;
using WooCoo.ObjectModel.ManagedProperty;
using WooCoo.Reflection;
using WooCoo.Tools;

namespace WooCoo.DataPortal.WebHost.ODataServer
{
    /// <summary>
    /// 定义一个使用OData数据格式的控制器基类.
    /// </summary>
    [ApiExplorerSettings(IgnoreApi = true), ODataPortalAction, ODataFormatting]
    public abstract class ODataPortalControllerBase : ApiController
    {
        private static readonly MethodInfo getRespositoryMI = (MethodInfo)typeof(IRepositoryFactory).GetMember("GetRepository").Single((MemberInfo m) => ((MethodInfo)m).GetGenericArguments().Count<Type>() == 1);
        /// <summary>
        /// 对应获取实体列表方式的请求
        /// </summary>
        /// <param name="contactName"></param>
        /// <param name="queryOptions"></param>
        /// <returns></returns>
        [HttpGet, ODataNullValue]
        public virtual HttpResponseMessage Get(string contactName, WCODataQueryOptions queryOptions)
        {
            try
            {
                Entity entity = System.Activator.CreateInstance(queryOptions.Context.ElementClrType) as Entity;
                if (entity == null)
                {
                    throw NotImplementedResponseException(this, contactName);
                }
                IQueryable queryApplied = queryOptions.ApplyTo(entity.GetRepository().AsQueryable());
                if (queryOptions.SelectExpand == null)
                    foreach (var item in queryApplied)
                    {
                        (item as ITrackStatus).MarkClean();
                    }
                return Request.CreateEntityResponse(HttpStatusCode.OK, queryApplied);
            }
            catch (Exception ex)
            {
                return Request.CreateODataErrorResponse(HttpStatusCode.InternalServerError,
                   new ODataError()
                   {
                       Message = ex.Message,
                       InnerError = new ODataInnerError(ex)
                   });
            }
        }
        /// <summary>
        /// 根据实体主键获取实体请求
        /// </summary>
        /// <param name="contactName"></param>
        /// <param name="key"></param>
        /// <param name="queryOptions"></param>
        /// <returns></returns>
        [HttpGet, ODataNullValue]
        public virtual HttpResponseMessage Get(string contactName, string key, WCODataQueryOptions queryOptions)
        {
            try
            {

                var entityType = queryOptions.Context.ElementClrType;
                //Entity entity = System.Activator.CreateInstance(queryOptions.Context.ElementClrType) as Entity;                
                var repository = getRespositoryMI.MakeGenericMethod(new Type[] { entityType }).Invoke(RepositoryFactoryHost.Factory, null);
                if (repository == null)
                {
                    throw NotImplementedResponseException(this, contactName);
                }
                var result = (repository as IRepository).Find(key);
                if (result == null)
                    return Request.CreateErrorResponse(HttpStatusCode.NotFound, "未找到");
                //IQueryable queryApplied = queryOptions.ApplyTo(entity.GetRepository().AsQueryable());
                if (result is ITrackStatus)
                    (result as ITrackStatus).MarkClean();
                return Request.CreateEntityResponse(HttpStatusCode.OK, result);//.CreateSingleEntityResponseFromRuntimeType(queryApplied);
            }
            catch (Exception ex)
            {
                return Request.CreateODataErrorResponse(HttpStatusCode.InternalServerError,
                    new ODataError()
                    {
                        Message = ex.Message,
                        InnerError = new ODataInnerError(ex)
                    });
            }
        }
        /// <summary>
        /// 新增实体请求
        /// </summary>
        /// <param name="contactName"></param>
        /// <param name="entityOrList"></param>
        /// <returns></returns>
        [HttpPost, ODataNullValue]
        public virtual HttpResponseMessage Post(string contactName, [FromJsonBody]object entityOrList)
        {
            DataPortalFacade facade = new DataPortalFacade();
            DataPortalContext dpc = GetContextFromRequest(Request);
            try
            {
                facade.Create(entityOrList, dpc);
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (Exception ex)
            {
                return Request.CreateODataErrorResponse(HttpStatusCode.InternalServerError,
                    new ODataError()
                    {
                        Message = ex.Message,
                        InnerError = new ODataInnerError(ex)
                    });
            }
        }

        /// <summary>
        /// 数据更新请求，针对WooCoo Entity将根据状态自动选择更新、插入、删除
        /// </summary>
        /// <param name="contactName"></param>
        /// <param name="entityOrList"></param>
        /// <returns></returns>
        [HttpPut, ODataNullValue]
        public virtual HttpResponseMessage Put(string contactName, [FromJsonBody]object entityOrList)
        {
            DataPortalFacade facade = new DataPortalFacade();
            DataPortalContext dpc = GetContextFromRequest(Request);
            try
            {
                facade.Update(entityOrList, dpc);
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (Exception ex)
            {
                return Request.CreateODataErrorResponse(HttpStatusCode.InternalServerError,
                    new ODataError()
                    {
                        Message = ex.Message,
                        InnerError = new ODataInnerError(ex)
                    });
            }
        }

        [HttpDelete, ODataNullValue]
        public virtual HttpResponseMessage Delete(string contactName, string key)
        {
            IEdmModel model = Request.GetEdmModel();
            Type entityClrType = EdmLibHelpers.GetClrType(Request.GetODataPath().EntitySet.ElementType, model);
            var param = Expression.Parameter(entityClrType, "x");
            var keyProperty = entityClrType.GetProperty("ID");
            var left = Expression.MakeMemberAccess(param, keyProperty);
            object value = TypeHelper.CoerceValue(keyProperty.PropertyType, key);
            ConstantExpression constantExpression = Expression.Constant(value);

            Expression filter = Expression.MakeBinary(ExpressionType.Equal, left, constantExpression);
            DataPortalFacade facade = new DataPortalFacade();
            DataPortalContext dpc = GetContextFromRequest(Request);
            try
            {
                facade.Delete(entityClrType, filter, dpc);
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (Exception ex)
            {
                return Request.CreateODataErrorResponse(HttpStatusCode.InternalServerError,
                    new ODataError()
                    {
                        Message = ex.Message,
                        InnerError = new ODataInnerError(ex)
                    });
            }
        }

        public static HttpResponseException NotImplementedResponseException(ApiController controller, string requestType)
        {
            return new HttpResponseException(controller.Request.CreateResponse(HttpStatusCode.NotImplemented,
                                                                              new ODataError
                                                                              {
                                                                                  Message = string.Format("{0} does not support {1} requests.", controller.GetType().FullName, requestType),
                                                                                  ErrorCode = requestType + " requests not supported"
                                                                              }));
        }

        [HttpPost]
        public HttpResponseMessage Excute([FromJsonBody]ActionInfo actionInfo)
        {
            try
            {
                DataPortalResult result = null;
                DataPortalContext dpc = GetContextFromRequest(Request);
                DataPortalFacade facade = new DataPortalFacade();
                //object so = ServiceObjectFactory.Instance.GetServiceObject(requestMethod.InType, requestMessage.RequestMethod.MethodName);
                object so;
                if (WooCoo.IoC.Container.Current.TryGetExports(actionInfo.ContractName, out so))
                {
                    var types = MethodCaller.GetCachedMethodParamesterTypes(so.GetType(), actionInfo.MethodName, actionInfo.Parameters.Count());
                    for (int i = 0; i < types.Length; i++)
                    {
                        if (actionInfo.Parameters[i] is Newtonsoft.Json.Linq.JContainer)
                            actionInfo.Parameters[i] = Newtonsoft.Json.JsonConvert.DeserializeObject(actionInfo.Parameters[i].ToString(), types[i]);
                        else
                            actionInfo.Parameters[i] = TypeHelper.CoerceValue(types[i], actionInfo.Parameters[i]);
                    }
                    result = facade.Execute(so, actionInfo, dpc);
                    return Request.CreateJsonResponse<DataPortalResult>(HttpStatusCode.OK, result); ;
                }
                //未找到实现类
                HttpResponseMessage hrm = Request.CreateODataErrorResponse(HttpStatusCode.InternalServerError,
                     new ODataError()
                     {
                         Message = String.Format("未能找到{0}的实现", actionInfo.ContractName),
                         ErrorCode = String.Format("未能找到{0}的实现", actionInfo.ContractName)
                     });
                return hrm;
            }
            catch (Exception ex)
            {
                return Request.CreateODataErrorResponse(HttpStatusCode.InternalServerError,
                    new ODataError()
                    {
                        Message = GetException(ex).Message,
                        InnerError = new ODataInnerError(ex)
                    });
            }
        }

        /// <summary>
        /// 从HttpRequest中获取上下文信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static DataPortalContext GetContextFromRequest(HttpRequestMessage request)
        {
            DataPortalContext result = new DataPortalContext();
            IEnumerable<string> val;
            if (request.Headers.TryGetValues("WooCooSessionID", out val))
            {
                result.SessionID = val.First();
            }
            if (request.Headers.TryGetValues("WooCooTransType", out val))
            {
                result.TransactionType = TypeHelper.CoerceValue<TransactionType>(val.First());
            }
            else
                result.TransactionType = TransactionType.DbTransaction;

            return result;
        }

        private static System.Exception GetException(Exception topEx)
        {
            if (null == topEx.InnerException)
            {
                //return new System.Exception("远程异常类型:" + topEx.Source + System.Environment.NewLine
                //  + "异常信息:" + topEx.Message + System.Environment.NewLine
                //  + "异常跟踪:" + topEx.StackTrace
                //  );
                return topEx;

            }
            System.Exception innerException = GetException(topEx.InnerException);
            return new System.Exception("远程错误信息:", innerException);
        }
    }
}
